加载类时未初始化java静态变量
我有一个关于初始化的有趣问题。我有以下代码:
public class ErrorLookupProvider {
private static final ErrorLookupProvider INSTANCE = new ErrorLookupProvider();
private static Map<Long, List<String>> map = new HashMap<Long, List<String>>();
private ErrorLookupProvider() {
init();
}
private void init() {
map.put(123L, ImmutableList.of("abc", "def"));
}
public static ErrorLookupProvider getInstance() {
return INSTANCE;
}
}
现在,当我调用ErrorLookupProvider.getInstance()
时,我点击了一个NPE。init()
内的映射未使用新的HashMap
进行初始化
如果我将map
的声明更改为final,那么我看到它已初始化。或者,即使我删除static并将其作为private Map<.....>
的私有类变量,它也可以工作
我还没弄明白为什么会这样。有人能解释一下这里发生了什么吗
# 1 楼答案
切换映射和单例实例初始化的顺序
静态初始化按照在源代码中遇到的顺序进行
参见JLS 12.4.2 Detailed Initialization Procedure,第6步(第
final
部分)和第9步(第“顺序”部分)(单例实现和在ctor中处理静态,另一期。)
# 2 楼答案
引用http://javapapers.com/core-java/explain-the-final-keyword-in-java/
这就是为什么当宣布为最终版本时,它会被草签
# 3 楼答案
补充:订单很重要。将静态映射的声明放在实例声明之前。Java编译器在排序方面有点愚蠢
因为map是静态的,所以它在
ErrorLookupProvider
的所有实例之间共享。因此,在构造函数中使用它可能是一个错误。如果您创建了多个ErrorLookupProvider,您将多次冗余地添加到地图中。相反,在静态初始值设定项块中初始化它。或者,如果它真的意味着在ErrorLookupProvider
的实例之间是独立的,那么不要将其设置为静态的